package assg.funnycells.cells;

import gnu.getopt.Getopt;
import java.lang.reflect.*;
import java.net.*;
import java.io.*;

import assg.funnycells.util.*;
import assg.funnycells.server.CellState;

public class FunnyCell {
    static Configuration config;
    Logger logger;

    int x, y, type, energy;
    String cellId;
    CellularProcesses cellularProc;
    String infectedClass, newInfectedClass, antibodyClass, newAntibodyClass;

    public FunnyCell(int x, int y, int type, int energy, String cellId,
		     String infectedClass, String newInfectedClass, String antibodyClass, String newAntibodyClass) {
	logger = Logger.getDefaultLogger();
	this.x = x;
	this.y = y;
	this.type = type;
	this.energy = energy;
	this.cellId = cellId;
	this.infectedClass = infectedClass;
	this.newInfectedClass = newInfectedClass;
	this.antibodyClass = antibodyClass;
	this.newAntibodyClass = newAntibodyClass;
    }

    public void start() {
	try {
	    String serverIP = config.getVal(Configuration.SERVER, Configuration.IP);
	    int serverPort = config.getIntVal(Configuration.SERVER, Configuration.PORT);
	    String className = config.getVal(Configuration.CLASSES, Configuration.SUSCEPTIBLECELL);

	    switch(type) {
	    case assg.funnycells.server.CellState.NEWINFECTED:
		className = newInfectedClass;
		break;
	    case assg.funnycells.server.CellState.NEWANTIBODY:
		className = newAntibodyClass;
		break;
	    case assg.funnycells.server.CellState.INFECTED:
		className = infectedClass;
		break;
	    case assg.funnycells.server.CellState.ANTIBODY:
		className = antibodyClass;
		break;
	    case assg.funnycells.server.CellState.ENERGY:
		className = config.getVal(Configuration.CLASSES, Configuration.ENERGYCELL);
		break;
	    }

	    Socket soc = new Socket(serverIP, serverPort);
	    RateLimBufferedReader in = new RateLimBufferedReader(new InputStreamReader(soc.getInputStream()), this);
	    in.initClassNames(infectedClass, newInfectedClass, antibodyClass, newAntibodyClass);
	    RateLimPrintWriter out = new RateLimPrintWriter(soc.getOutputStream(),
							    config.getIntVal(Configuration.PARAMS, Configuration.MOLTIMEOUT) * 
							    config.getIntVal(Configuration.PARAMS, Configuration.RATETIMEOUT));
	    logger.info(cellId + " - connected to coordinator (" + x + ", " + y + "): " + type);
	    Class<?> cellClass = Class.forName(className);
	    Constructor cellConstructor = cellClass.getConstructor(Integer.class, Integer.class, 
								   String.class, RateLimBufferedReader.class, RateLimPrintWriter.class);

	    ChemOperations.sendMessage(soc, new String[]{
					   assg.funnycells.server.Cell.NEWCELL + "=" + cellId,
					   "type=" + CellState.invNewType(type),
					   "x=" + x,
					   "y=" + y,
					   "energy=" + energy});
	    // read NEWCEll reply
	    String str;
	    while((str = in.readLine()) != null && !str.equals(""));

	    cellularProc = (CellularProcesses)(cellConstructor.newInstance(new Object[]{type, energy, cellId, in, out}));
	    logger.info(cellId + " - cell instantiated. starting");
	    cellularProc.startCell();
	} catch(Exception e) {
	    logger.error("Cell error: Exiting...", e);
	}
    }

    public void exit() {
	System.exit(0);
    }

    public static Configuration getConfiguration() {
	return config;
    }

    public static void main(String[] args) {
	Getopt g = new Getopt("funnycell", args, "c:x:y:t:i:l:r:g:");
	int c;
	String configPath = "funnycells.conf";
	int x = -1, y = -1, type = -1;
	String cellId = "";
	Logger logger = Logger.getDefaultLogger();
	String infectedClass = null, antibodyClass = null, newInfectedClass = null, newAntibodyClass = null;

	while((c = g.getopt()) != -1) {
	    switch(c) {
	    case 'c':
		configPath = g.getOptarg();
		break;
	    case 'x':
		x = StringOperations.parseInt(g.getOptarg());
		break;
	    case 'y':
		y = StringOperations.parseInt(g.getOptarg());
		break;
	    case 't':
		type = StringOperations.parseInt(g.getOptarg());
		break;
	    case 'i':
		cellId = g.getOptarg();
		break;
	    case 'l':
		int logLevel = StringOperations.parseInt(g.getOptarg());
		if(logLevel > -1)
		    Logger.getDefaultLogger().setLogLevel(logLevel);
		break;
	    case 'r':
		infectedClass = newInfectedClass = g.getOptarg();
		break;
	    case 'g':
		antibodyClass = newAntibodyClass = g.getOptarg();
		break;
	    }
	}

	logger.info("Read arguments: c = " + configPath + ", x = " + x + ", y = " + y + ", t = " + type + ", cellId = " + cellId);

	if(x == -1 || y == -1 || type == -1 || cellId.equals("")) {
	    logger.info("usage: -x X -y Y -t type -i cellId");
	    System.exit(-1);
	}

	config = new Configuration(configPath);
	if(infectedClass == null || antibodyClass == null) {
	    newInfectedClass = config.getVal(Configuration.CLASSES, Configuration.NEWINFECTEDCELL);
	    infectedClass = config.getVal(Configuration.CLASSES, Configuration.INFECTEDCELL);
	    newAntibodyClass = config.getVal(Configuration.CLASSES, Configuration.NEWANTIBODYCELL);
	    antibodyClass = config.getVal(Configuration.CLASSES, Configuration.ANTIBODYCELL);
	}

	(new FunnyCell(x, y, type, 
		       StringOperations.parseInt(config.getVal(Configuration.PARAMS, Configuration.MAXENERGY)), 
		       cellId,
		       infectedClass, newInfectedClass, antibodyClass, newAntibodyClass)).start();
    }

}